package com.passion.fs.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.passion.common.constant.CommonConstant;
import com.passion.common.enums.BaseEnums;
import com.passion.common.exception.MyException;
import com.passion.common.util.FileUtil;
import com.passion.fs.context.LocalFsContext;
import com.passion.fs.dao.FileInfoDao;
import com.passion.fs.entity.FileInfo;
import com.passion.fs.service.FileInfoService;
import com.passion.fs.util.FileEncDecUtil;
import com.passion.fs.util.LocalFsTreeUtil;
import com.passion.fs.vo.DirInfoVO;
import com.passion.fs.vo.DtreeNode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 注意service层方法命名规范：
 * 分页查询方法    （使用 page 做前缀）
 * 查询单个对象方法（使用 get 做前缀）
 * 查询多个对象方法（使用 list 做前缀）
 * 统计数量方法    （使用 count 做前缀）
 * 新增方法        （使用 save / insert 做前缀）
 * 删除方法        （使用 remove / delete 做前缀）
 * 修改方法        （使用 update 做前缀）
 * 
 * 【t_fs_file_info】表服务接口
 * @author ZhuYin<zyexplorej2ee@sina.com>
 * @since  2025年01月06日
 */
@Slf4j
@Service
public class FileInfoServiceImpl extends ServiceImpl<FileInfoDao, FileInfo> implements FileInfoService {

    /**
     * 方法说明：根据文件在系统中的唯一名称来获取文件信息对象
     *
     * @param fileSysName 文件在文件系统中的唯一名称
     * @return 【t_fs_file_info】表实体类;
     * @author ZhuYin
     * @since 2025年01月07日
     */
    @Override
    public FileInfo getFileInfo(String fileSysName) {
        LambdaQueryWrapper<FileInfo> wrapper = Wrappers.<FileInfo>lambdaQuery()
                .eq(FileInfo::getFileSysName, fileSysName)
                .eq(FileInfo::getDelFlag, BaseEnums.DelFlag.NORMAL.getCode())
                .last("limit 1");
        return super.getOne(wrapper);
    }

    /**
     * 方法说明：根据目录id来获取该目录下的所有目录或文件对象
     *
     * @param dirId 目录id
     * @return 返回该目录下的所有目录或文件对象集合
     * @author ZhuYin
     * @since 2025年01月07日
     */
    @Override
    public List<FileInfo> listByDirId(String dirId) {
        if (StringUtils.isBlank(dirId) || CommonConstant.DIR_SPLIT.equals(dirId)) {
            // 传空或者传 / 表示查询根目录下的所有目录或文件
            dirId = CommonConstant.ROOT_PARENT_ID;
        } else {
            dirId = dirId.substring(dirId.lastIndexOf(CommonConstant.DIR_SPLIT) + 1);
        }
        LambdaQueryWrapper<FileInfo> wrapper = Wrappers.<FileInfo>lambdaQuery()
                .eq(FileInfo::getParentId, dirId)
                .eq(FileInfo::getDelFlag, BaseEnums.DelFlag.NORMAL.getCode())
                .orderByDesc(FileInfo::getCreateTime);
        List<FileInfo> list = super.list(wrapper);
        if (CollectionUtils.isNotEmpty(list)) {
            // 将所有文件夹的文件文件扩展类型设置为 dir ，为了让前端用 dir.png 显示
            list.stream().filter(x -> StringUtils.isBlank(x.getFileExt())).forEach(x -> x.setFileExt("dir"));
        }
        return list;
    }

    /**
     * 方法说明：查询目录信息
     *
     * @param dirId 目录id
     * @return 类描述：目录信息VO
     * @author ZhuYin
     * @since 2025年01月08日
     */
    @Override
    public DirInfoVO getDirInfo(String dirId) {
        if (StringUtils.isBlank(dirId)) {
            throw new MyException("目录id不能为空");
        }
        FileInfo fileInfo = super.getById(dirId);
        if (fileInfo == null) {
            throw new MyException("目录不存在");
        }
        String parentId = fileInfo.getParentId();
        // 当前目录名称
        String tempId = dirId;
        LinkedHashSet<FileInfo> parentNodesSet = Sets.newLinkedHashSet();
        // 添加当前节点进去也算父节点
        parentNodesSet.add(fileInfo);
        if (CommonConstant.ROOT_PARENT_ID.equals(tempId)) {
            // 如果父节点就是根节点，则再加入根节点
            FileInfo rootNode = FileInfo.builder().id(CommonConstant.ROOT_PARENT_ID).showName("").build();
            parentNodesSet.add(rootNode);
        } else {
            // 如果父节点不是根节点，则加入递归父节点
            while (!CommonConstant.ROOT_PARENT_ID.equals(tempId)) {
                FileInfo node = super.getById(tempId);
                tempId = node.getParentId();
                parentNodesSet.add(node);
            }
        }
        List<FileInfo> parentNodesList = new ArrayList<>(parentNodesSet);
        Collections.reverse(parentNodesList);
        String displayName = "/" + parentNodesList.stream().map(FileInfo::getShowName).collect(Collectors.joining("/"));
        String displayDirId = "/" + parentNodesList.stream().map(FileInfo::getId).collect(Collectors.joining("/"));
        return DirInfoVO.builder()
                .dirId(dirId)
                .parentDirId(parentId)
                .displayName(displayName)
                .displayDirId(displayDirId)
                .build();
    }

    /**
     * 方法说明：目录管理
     *
     * @return 返回目录管理集合
     * @author ZhuYin
     * @since 2025年01月08日
     */
    @Override
    public List<DtreeNode> getTree() {
        LambdaQueryWrapper<FileInfo> wrapper = Wrappers.<FileInfo>lambdaQuery()
                .eq(FileInfo::getDelFlag, BaseEnums.DelFlag.NORMAL.getCode());
        List<FileInfo> list = LocalFsContext.fileInfoService.list(wrapper);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 组成一棵树
        return buildTree(list);
    }

    /**
     * 方法说明：获取文件夹树
     *
     * @return 返回文件夹树
     * @author ZhuYin
     * @since 2025年01月09日
     */
    @Override
    public List<DtreeNode> getDirTree() {
        LambdaQueryWrapper<FileInfo> wrapper = Wrappers.<FileInfo>lambdaQuery()
                .eq(FileInfo::getDelFlag, BaseEnums.DelFlag.NORMAL.getCode())
                .eq(FileInfo::getDirFlag, 1);
        List<FileInfo> list = LocalFsContext.fileInfoService.list(wrapper);
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 组成一棵树
        return buildTree(list);
    }

    /**
     * 方法说明：删除树节点
     *
     * @param id 树节点ID
     * @return true-成功 false-失败
     * @author ZhuYin
     * @since 2025年01月09日
     */
    @Override
    public boolean deleteTreeNode(String id) {
        FileInfo fileInfo = super.getById(id);
        if (fileInfo == null) {
            return true;
        }
        List<String> nodeIds = Lists.newArrayList(id);
        Integer dirFlag = fileInfo.getDirFlag();
        if (Objects.equals(dirFlag, 1)) {
            // 如果是文件夹，则递归删除其下的所有节点
            this.getSubIdList(id, nodeIds);
        }
        LambdaUpdateWrapper<FileInfo> wrapper = Wrappers.<FileInfo>lambdaUpdate().in(FileInfo::getId, nodeIds);
        wrapper.set(FileInfo::getDelFlag, BaseEnums.DelFlag.DELETED.getCode());
        return super.update(wrapper);
    }

    /**
     * 方法说明：下载文件
     *
     * @param id       文件id
     * @param response 相应对象
     * @author ZhuYin
     * @since 2025年01月09日
     */
    @Override
    public void download(String id, HttpServletResponse response) {
        FileInfo fileInfo = super.getById(id);
        if (fileInfo == null || !new File(fileInfo.getFilePath()).exists()) {
            throw new MyException("文件不存在");
        }
        // 文件存放的路径
        String filePath = fileInfo.getFilePath();
        String secretKey = fileInfo.getSecretKey();
        String tempName = filePath;
        String decryptSuffix = ".temp";
        if (StringUtils.isNotBlank(secretKey)) {
            // 解密后的临时文件路径
            tempName = filePath + decryptSuffix;
            try {
                // 解密得到解密后的临时文件
                FileEncDecUtil.decrypt(secretKey, new File(filePath), new File(tempName));
            } catch (Exception e) {
                log.error("[Local] decrypt file failed: {}", e.getMessage());
                throw new MyException("解密失败，请联系管理员");
            }
        }
        try {
            FileUtil.download(Files.newInputStream(Paths.get(tempName)), fileInfo.getShowName(), response);
        } catch (IOException e) {
            log.error("[Local] file download failed: {}", e.getMessage());
            throw new MyException("文件下载失败，请联系管理员");
        } finally {
            if (tempName.endsWith(decryptSuffix)) {
                // 删除临时文件
                FileUtil.del(tempName);
            }
        }
    }

    /**
     * 方法说明：查询父级的多有子级节点(递归)
     *
     * @param parentId 父级节点ID
     * @param idList   用来装子级节点的集合
     * @author ZhuYin
     * @since 2025年01月09日
     */
    private void getSubIdList(String parentId, List<String> idList) {
        // 查询菜单里面子菜单id
        LambdaQueryWrapper<FileInfo> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(FileInfo::getParentId, parentId);
        List<FileInfo> childIdList = super.list(wrapper);
        // 把childIdList里面菜单id值获取出来，封装idList里面，做递归查询
        if (CollectionUtils.isNotEmpty(childIdList)) {
            childIdList.forEach(item -> {
                // 封装idList里面
                idList.add(item.getId());
                // 递归查询
                this.getSubIdList(item.getId(), idList);
            });
        }
    }

    /**
     * 方法说明：将所有节点组成一棵树
     *
     * @param allNodes 所有节点
     * @return 返回树的根节点
     * @author ZhuYin
     * @since 2025年01月09日
     */
    public List<DtreeNode> buildTree(List<FileInfo> allNodes) {
        if (allNodes == null || allNodes.isEmpty()) {
            return Collections.emptyList();
        }
        List<DtreeNode> dtree = Lists.newArrayList();
        allNodes.forEach(node -> {
            DtreeNode dtreeNode = new DtreeNode();
            BeanUtils.copyProperties(node, dtreeNode);
            dtreeNode.setTitle(node.getShowName());
            if (node.getDirFlag() == 1) {
                dtreeNode.setIconClass(CommonConstant.DTREE_ICON_1);
            } else {
                dtreeNode.setIconClass(CommonConstant.DTREE_ICON_2);
            }
            dtree.add(dtreeNode);
        });
        // 构建树结构
        List<DtreeNode> treeNodes = dtree.stream()
                .filter(m -> Objects.equals(m.getParentId(), CommonConstant.ROOT_PARENT_ID))
                .peek((m) -> m.setChildren(getChildren(m, dtree)))
                .collect(Collectors.toList());
        // 计算文件夹下面的文件数量
        LocalFsTreeUtil.updateFileCount(treeNodes);
        return treeNodes;
    }

    /**
     * 递归查询子节点
     *
     * @param root      根节点
     * @param allNodes  所有节点
     * @return 根节点信息
     */
    private List<DtreeNode> getChildren(DtreeNode root, List<DtreeNode> allNodes) {
        return allNodes.stream()
                .filter(m -> Objects.equals(m.getParentId(), root.getId()))
                .peek((m) -> m.setChildren(getChildren(m, allNodes)))
                .collect(Collectors.toList());
    }
}